package utils;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Vector;

import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import parameters.DefaultParameterValues;
import parameters.RlmServerParameters;

//import org.apache.commons.codec.binary.Hex;

/**
 * Network Interface Card Manager class.
 * 
 * @author Uros Krkic
 */
public class NICManager {

	public static final String NO_MAC_ADDRESS = DefaultParameterValues.MAC_ADDRESS;
	
	private static Vector<NICInfo> adapters = new Vector<NICInfo>();
	
	private static final NICInfo autoSelectAdapter = new NICInfo("auto_select", "Auto Select Network Adapter", NO_MAC_ADDRESS);
	
	private NICManager() { }
	
	
	/**
	 * This method returns MAC address of network interface if such exists.
	 * @param netInterface - network interface to resolve MAC address
	 * @return MAC address
	 * @throws SocketException
	 * @author Uros Krkic
	 */
	private static String getInterfaceMACAddress(NetworkInterface netInterface) throws SocketException {
		
		if (netInterface.getHardwareAddress() == null) {
			return NO_MAC_ADDRESS;
		}
		
		byte[] macByteArray = netInterface.getHardwareAddress();
//		char[] macCharArray = Hex.encodeHex(macByteArray);
		char[] macCharArray = bytesToHexChars(macByteArray);
		
		StringBuffer macBuffer = new StringBuffer();
		
		for (int i = 0; i < macCharArray.length; i++) {
			macBuffer.append(macCharArray[i]);
			if ( (i % 2 == 1) && (i < macCharArray.length-1) ) {
				macBuffer.append("-");
			}
		}
		
		return macBuffer.toString().toUpperCase();
	}
	
	
	/**
	 * This method reads all available NIC adapters, creates NICInfo objects and
	 * stores them into adapters vector.
	 * @author Uros Krkic
	 */
	public static void readAdapters() {
		adapters.add(autoSelectAdapter);
		try {
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			
			while (interfaces.hasMoreElements()) {
				NetworkInterface interFace = interfaces.nextElement();
				
				// only in Java 1.6
//				if (interFace.isLoopback())
//					continue;
				
				NICInfo nicInfo = new NICInfo( interFace.getName(), interFace.getDisplayName(), getInterfaceMACAddress(interFace) );
				
				Enumeration<InetAddress> addresses = interFace.getInetAddresses();
				while (addresses.hasMoreElements()) {
					InetAddress address = addresses.nextElement();

					nicInfo.addNICAddress(address);
					
					if (address.isLoopbackAddress())
						continue;
				}
				
				adapters.add(nicInfo);
			}
			

			// added for MAC
			String oldIPAddress = RlmServerParameters.getNetworkInterfaceToConnect(true);
			NICInfo nicInfo = getNICByAddress(oldIPAddress);
			
			if (nicInfo == null) {
				// IP address has changed !!!
				System.out.println("CHANGED " + oldIPAddress);
				// get bound NIC adapter
				nicInfo = getNICByMACAddress(RlmServerParameters.getMACAddress());
				
				if (nicInfo.getNICAddresses().size() > 1) {
					// NIC has multiple IP addresses
					// let to choose IP address
					MACPanel macPanel = new MACPanel(nicInfo);
					int option = -1;
					while (option == -1) {
						option = JOptionPane.showOptionDialog(null, macPanel, "IP Address Change Dialog", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null);
					}
					RlmServerParameters.setNetworkInterfaceToConnect(macPanel.selectedIPAddress);
				}
				else {
					try {
						RlmServerParameters.setNetworkInterfaceToConnect(nicInfo.getNICAddressesAsString().firstElement());
					}
					catch (NoSuchElementException nsee) {
						RlmServerParameters.setNetworkInterfaceToConnect(DefaultParameterValues.NIC_AUTO_SELECT);
					}
				}
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Panel that shows combobox of IP addresses if NIC adapter has more than one IP address.
	 * @author Uros Krkic
	 */
	@SuppressWarnings("serial")
	private static class MACPanel extends JPanel {
		
		private NICInfo nicInfo;
		private JLabel label;
		private JComboBox comboBox = null;
		private String selectedIPAddress = null;
		
		public MACPanel(NICInfo nicInfo) {
			super();
			this.nicInfo = nicInfo;
			
			this.setLayout(new GridLayout(4,1));
			
			initialization();
		}
		
		private void initialization() {
			label = new JLabel("RLM has detected that your IP address has been changed !");
			add(label);
			label = new JLabel("Also, RLM detected that NIC adapter has multiple IP addresses !");
			add(label);
			label = new JLabel("Please, select appropriate IP address:");
			add(label);
			add(getComboBox());
		}
		
		private JComboBox getComboBox() {
			if (comboBox == null) {
				comboBox = new JComboBox(nicInfo.getNICAddressesAsString());
				selectedIPAddress = nicInfo.getNICAddressesAsString().firstElement();
				comboBox.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						selectedIPAddress = comboBox.getSelectedItem().toString();
					}
				});
			}
			return comboBox;
		}
		
	}
	
	
	/**
	 * This method refreshes NIC adapter list.
	 * @author Uros Krkic
	 */
	public static void refreshAdapters() {
		adapters.clear();
		readAdapters();
	}
	
		
	/**
	 * This method returns NIC adapters list.
	 * @return NIC adapter list
	 * @author Uros Krkic
	 */
	public static Vector<NICInfo> getAdapters() {
		return adapters;
	}
	
	
	/**
	 * This method returns NICInfo object by IP address that belong to it.
	 * @param address - IP address
	 * @return NICInfo object
	 * @author Uros Krkic
	 */
	public static NICInfo getNICByAddress(String address) {
		
		for (NICInfo nicInfo : adapters) {
			if ( nicInfo.containsAddress(address) ) {
				return nicInfo;
			}
		}
		
		return null;
	}
	
	
	/**
	 * This method returns NICInfo object by MAC address that belong to it.
	 * @param macAddress - MAC address
	 * @return NICInfo object
	 * @author Uros Krkic
	 */
	public static NICInfo getNICByMACAddress(String macAddress) {
		for (NICInfo nicInfo : adapters) {
			if ( nicInfo.getNicMACAddress().equals(macAddress) ) {
				return nicInfo;
			}
		}
		
		return null;
	}
	
	
	public static char hihex(byte num) {
		int high = ((num >> 4) & 0x0F);

		high+=(high < 10)?'0':('A' - 10);

		return (char) high;
	}
	
	public static char lohex(byte num) {
		int low = (num & 0x0F);

		low+=(low < 10)?'0':('A' - 10);

		return (char) low;
	}
	
	public static char[] bytesToHexChars(byte[] data) {
		char[] res = new char[2 * data.length];
		for (int i = 0; i < data.length; i++) {
			res[2*i  ] = hihex(data[i]);
			res[2*i+1] = lohex(data[i]);
		}
		return res;
	}
	
}
